package myTeam;



import java.awt.Color;
import java.io.IOException;

import myTeam.Apelacao;
import myTeam.Predicao;
import robocode.HitWallEvent;
import robocode.Rules;
import robocode.ScannedRobotEvent;
import robocode.TeamRobot;
import myTeam.RobotColors;

public class MataTudo extends TeamRobot{

	ScannedRobotEvent lastEvent;

	private String nameEnemy;
	private double posXEnemy;
	private double posYEnemy;
	private double giroEnemy;
	private int lastTimeEnemy;
	
	private int direcao = 1;
	private double headingEnemy;
	private double enemySpeed;
	private long turnRate;
	private String[] parceiros;
	
	public void run(){
		
		parceiros = getTeammates();
		setColors(new Color(90, 55, 55), Color.white, Color.blue);
		setAdjustRadarForGunTurn(true);
		setAdjustGunForRobotTurn(true);
		turnRadarRightRadians(2 * Math.PI);

		setMaxVelocity(5);
		opcao(3);
		
		while(true){
			lastTimeEnemy++;
			
			changeSpeed();
			doMoviment();

			doScanner();
			
			doGun();
			doFire();

			//doMoviment();
			//doFire();

			execute();
		}
	}

	private void doFire() {
		if(getEnergy() > 15){
			setFire(Math.min(400 / getDistanciaEuclidiana(getX(),getY(),advinharXEnemy(),advinharYEnemy()), 3));
		}else{
			setFire(Rules.MIN_BULLET_POWER);
		}

	}

	private void changeSpeed() {
		if(isMax() && getTime() % 70 == 0){
			setMaxVelocity(6);
		}else{
			double v = getVelocity() + Rules.ACCELERATION * 5;
			setMaxVelocity(v);
		}
	}


	private boolean isMax() {
		if(getVelocity() == Rules.MAX_VELOCITY){
			return true;
		}else{
			return false;
		}
	}

	@Override
	public void onScannedRobot(ScannedRobotEvent event) {
		if(lastEvent == null){
			lastEvent = event;
		}

		if(isTeammate(event.getName())){
			brodcastPredicao(event);
			return;

		}else{

			turnRate = getTurnRate(getHeadingRadians(), lastEvent.getHeadingRadians(), getTime(), lastEvent.getTime());

			double giroRobo = (getHeadingRadians() + event.getBearingRadians()) % (2 * Math.PI);
			if(nameEnemy == null || lastTimeEnemy > 5){
				nameEnemy = event.getName();
			}
			
			if(nameEnemy != null && nameEnemy.equals(event.getName())){
				posXEnemy = getX() + Math.sin(giroRobo) * event.getDistance();
				posYEnemy = getY() + Math.cos(giroRobo) * event.getDistance();
				giroEnemy = event.getBearingRadians();
				headingEnemy = event.getHeadingRadians();
				enemySpeed = event.getVelocity();
				lastTimeEnemy = 0;
				
				lastEvent = event;
			}
			//opcao(1);
			brodcastPredicao(event);

		}

	}

	private void brodcastPredicao(ScannedRobotEvent event) {
		double giroRobo = (getHeadingRadians() + event.getBearingRadians()) % (2 * Math.PI);
		
		double posX = getX() + Math.sin(giroRobo) * event.getDistance();
		double posY = getY() + Math.cos(giroRobo) * event.getDistance();
		double giroEnemy = event.getBearingRadians();
		double headingEnemy = event.getHeadingRadians();
		double enemySpeed = event.getVelocity();
		Predicao pred = new Predicao(posX,posY,giroEnemy, headingEnemy,enemySpeed, getTime(), event.getName());

		try {
			//broadcastMessage(pred);
			for(String i : parceiros){
				sendMessage(i, pred);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void opcao(int op){

		switch(op){

		case 1:

			Predicao pred = new Predicao(posXEnemy,posYEnemy,giroEnemy, headingEnemy,enemySpeed, turnRate, "");

			try {
				for(String i : parceiros){
					sendMessage(i, pred);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}

			break;

		case 3:
			RobotColors c = new RobotColors();

			c.bodyColor = Color.black;
			c.gunColor = Color.blue;
			c.radarColor = Color.gray;
			c.scanColor = Color.yellow;
			c.bulletColor = Color.red;
			try {

				broadcastMessage(c);
				for(String i : parceiros){
					sendMessage(i, c);
				}
			} catch (IOException e) {
				// 
				e.printStackTrace();
			}
			break;
		default:
			return;
		}
	}
	public void doMoviment(){

		if (getTime() % 30 == 0)  { 		
			direcao *= -1;		
			setAhead(direcao * 400);
		}
		setTurnRightRadians(giroEnemy + (Math.PI / 2));

	}


	public void onHitWall(HitWallEvent event) {
		setMaxVelocity(Rules.MAX_VELOCITY);
		turnRightRadians(lastEvent.getBearingRadians());
		ahead(200);
	}

	public double advinharXEnemy(){
		return posXEnemy + (enemySpeed * Math.sin(headingEnemy + turnRate));
	}

	public double advinharYEnemy(){
		return posYEnemy + (enemySpeed * Math.cos(headingEnemy + turnRate));
	}

	public long getTurnRate(double enemyHeading, double lastEnemyHeading,long timeNow, long timeBefore){
		return (long) ((enemyHeading - lastEnemyHeading) / (timeNow - timeBefore));
	}

	public void doScanner() {
		double deslocamentoRadar;
		//se por um intervalo de tempo não encontrar o inimigo
		//gira o radar
		if (lastTimeEnemy > 4) { 	
			deslocamentoRadar = 360;		
		} else {	

			//seta a quantidade necessária para rotação do radar de acordo com a posicao do inimigo
			deslocamentoRadar = getRadarHeadingRadians() - absbearing(getX(),getY(),posXEnemy,posYEnemy);

			if (deslocamentoRadar < 0)
				deslocamentoRadar -= Math.PI/8;
			else
				deslocamentoRadar += Math.PI/8; 
		}

		deslocamentoRadar = Math.PI/4;
		//gira o radar
		setTurnRadarLeftRadians(NormaliseBearing(deslocamentoRadar));
	}

	public void doGun() {

		//predição do inimigo e rotação da mira para ele
		double rotacaoArma = getGunHeadingRadians() - absbearing(getX(),getY(),advinharXEnemy(),advinharYEnemy());
		setTurnGunLeftRadians(NormaliseBearing(rotacaoArma));
	}


	public double absbearing( double x1,double y1, double x2,double y2 )
	{
		double xo = x2-x1;
		double yo = y2-y1;
		double h = getDistanciaEuclidiana( x1,y1, x2,y2 );
		if( xo > 0 && yo > 0 )
		{
			return Math.asin( xo / h );
		}
		if( xo > 0 && yo < 0 )
		{
			return Math.PI - Math.asin( xo / h );
		}
		if( xo < 0 && yo < 0 )
		{
			return Math.PI + Math.asin( -xo / h );
		}
		if( xo < 0 && yo > 0 )
		{
			return 2.0*Math.PI - Math.asin( -xo / h );
		}
		return 0;
	}

	public double getDistanciaEuclidiana( double x1,double y1, double x2,double y2 )
	{
		double xo = x2-x1;
		double yo = y2-y1;
		double h = Math.sqrt( xo*xo + yo*yo );
		return h;	
	}

	//normaliza o giro
	public double NormaliseBearing(double ang) {
		if (ang > Math.PI)
			ang -= 2 * Math.PI;
		if (ang < - Math.PI)
			ang += 2 * Math.PI;
		return ang;
	}

}
